﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Expression.Interactivity.Input;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Data;
using System.Reflection;

namespace Redbrick.Silverlight.UI
{

    using i = System.Windows.Interactivity;
    public class KeystrokeCommandTrigger : KeyTrigger
    {
        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(string), typeof(KeystrokeCommandTrigger), new PropertyMetadata(new PropertyChangedCallback(KeystrokeCommandTrigger.OnCommandChanged)));
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(KeystrokeCommandTrigger), null);
        public static readonly DependencyProperty GestureProperty = DependencyProperty.RegisterAttached("Gesture", typeof(string), typeof(KeystrokeCommandTrigger), new PropertyMetadata(new PropertyChangedCallback(KeystrokeCommandTrigger.OnGestureChanged)));

        private static readonly Regex gestureRx = new Regex(@"^\w+(\s*\+\s*\w+)*$", RegexOptions.IgnoreCase);

        private static Dictionary<string, ModifierKeys> modifierMappings = new Dictionary<string, ModifierKeys>()
        {
		    { "ctrl", ModifierKeys.Control }
        };

        private static Dictionary<string, Key> keyMappings = new Dictionary<string, Key>()
        {
		    { "del", Key.Delete },
		    { "ins", Key.Insert },
		    { "esc", Key.Escape }
        };

        protected override void OnAttached()
        {
            base.OnAttached();
            FrameworkElement el = this.AssociatedObject as FrameworkElement;
            if (el != null)
            {
                if (el.DataContext != null) this.AttachCommand(el);
                else el.Loaded += new RoutedEventHandler(el_Loaded);
            }
        }

        void el_Loaded(object sender, RoutedEventArgs e)
        {
            FrameworkElement el = sender as FrameworkElement;
            if (el != null)
            {
                el.Loaded -= el_Loaded;
                AttachCommand(el);

            }
        }
        private void AttachCommand(FrameworkElement el)
        {
            if (el.DataContext != null)
            {
                Type typ = el.DataContext.GetType();
                PropertyInfo[] info = typ.GetProperties();
                string cmdName = GetCommand(this);
                if (!string.IsNullOrWhiteSpace(cmdName))
                {
                    var prop = info.FirstOrDefault(p => p.Name == cmdName);
                    if (prop != null)
                    {
                        ICommand cmd = prop.GetValue(el.DataContext, null) as ICommand;
                        if (cmd != null)
                        {
                            i.TriggerActionCollection actions = this.GetValue(ActionsProperty) as i.TriggerActionCollection;
                            if (actions != null)
                            {
                                CommandTriggerAction cta = new CommandTriggerAction()
                                {
                                    Command = cmd,
                                    CommandParameter = GetCommandParameter(this),
                                };
                                actions.Add((i.TriggerAction)cta);
                            }

                        }

                    }
                }
            }
        }
        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            string command = e.NewValue as string;
            i.TriggerActionCollection actions = d.GetValue(ActionsProperty) as i.TriggerActionCollection;
            CommandTriggerAction commandAction = null;

            //foreach (TriggerAction a in actions)
            //{

            //}

            //if (commandAction == null)
            //{
            //    actions.Add(new CommandTriggerAction()
            //    {
            //        Command = GetCommand(d),
            //        CommandParameter = GetCommandParameter(d)
            //    });
            //}
        }

        /// <summary>
        /// Here we are going to parse provided gesture into the set of modifier keys and the main key.
        /// </summary>
        private static void OnGestureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            string gesture = (string)e.NewValue;
            ModifierKeys modifierKeys = ModifierKeys.None;
            Key key = Key.None;

            ParseGesture(gesture, out modifierKeys, out key);

            d.SetValue(ModifiersProperty, modifierKeys);
            d.SetValue(KeyProperty, key);
        }

        /// <summary>
        /// Parses string gesture into the set of modifier keys and the main key.
        /// </summary>
        /// <param name="gesture">String gesture representation.</param>
        /// <param name="modifierKeys">Modifier keys retuen value.</param>
        /// <param name="key">Key return value.</param>
        public static void ParseGesture(string gesture, out ModifierKeys modifierKeys, out Key key)
        {
            if (string.IsNullOrEmpty(gesture))
                throw new ArgumentException("Gesture can not be empty string or null");

            if (!gestureRx.IsMatch(gesture))
                throw new ArgumentException("Wrong gesture format, should be something like 'Ctrl+E' or 'Ctrl+Shift+D'", "gesture");

            modifierKeys = ModifierKeys.None;
            key = Key.None;
            var tokens = gesture.Split('+');

            for (int i = 0; i < tokens.Length; i++)
            {
                var token = tokens[i].Trim().ToLower();

                // We have modifier keys at the beginning.
                if (i < tokens.Length - 1)
                {
                    try
                    {
                        ModifierKeys modifierKey = (modifierMappings.ContainsKey(token)) ?
                            modifierMappings[token] : (ModifierKeys)Enum.Parse(typeof(ModifierKeys), token, true);

                        modifierKeys |= modifierKey;
                    }
                    catch (ArgumentException)
                    {
                        throw new FormatException(string.Format("Could not recognize {0} key", token));
                    }
                }
                // This is the main key.
                else
                {
                    try
                    {
                        key = (keyMappings.ContainsKey(token)) ?
                            keyMappings[token] : (Key)Enum.Parse(typeof(Key), token, true);
                    }
                    catch (ArgumentException)
                    {
                        throw new FormatException(string.Format("Could not recognize {0} key", token));
                    }
                }
            }
        }

        public static string GetCommand(DependencyObject d)
        {
            return (string)d.GetValue(CommandProperty);
        }
        public static object GetCommandParameter(DependencyObject d)
        {
            return d.GetValue(CommandParameterProperty);
        }
        public static string GetGesture(DependencyObject d)
        {
            return (string)d.GetValue(GestureProperty);
        }

        public static void SetCommand(DependencyObject d, string command)
        {
            d.SetValue(CommandProperty, command);
        }
        public static void SetCommandParameter(DependencyObject d, object o)
        {
            d.SetValue(CommandParameterProperty, o);
        }
        public static void SetGesture(DependencyObject d, string gesture)
        {
            d.SetValue(GestureProperty, gesture);
        }
    }
}
